﻿//-----------------------------------------------------------------------
// <copyright company="www.3WayWebdesign.de">
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
//
// <author>Michael Morbach</author>
// <email>Michael@3waywebdesign.de</email>
// <web>http://www.3waywebdesign.de</web>
//-----------------------------------------------------------------------

namespace Tww.IBAN.IBANUtilities.Validation
{
    #region Imports

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Tww.IBAN.IBANUtilities.Constants;
    using Tww.IBAN.IBANUtilities.Converter;
    using Tww.IBAN.IBANUtilities.Model;
    using Tww.IBAN.IBANUtilities.Parser;


    #endregion

    public class DefaultIBANValidator : IIBANValidator
    {
        #region IIBANValidator Implementation

        /// <summary>
        /// Validates the checksum of the IBAN
        /// </summary>
        /// <param name="iban">IBAN to check</param>
        /// <returns>True/False</returns>
        public bool ValidateIBANChecksum( string iban )
        {
            var cleanedIban = CharTransformationChart.NormalizeIBAN( iban );
            var checksumiban = string.Format( "{0}{1}", String.Concat( cleanedIban.Skip( 4 ).Take( cleanedIban.Length - 4 ) ), String.Concat( cleanedIban.Take( 4 ) ) );
            checksumiban = CharTransformationChart.TransformToString( checksumiban );

            do
            {
                checksumiban = string.Format( "{0}{1}", int.Parse( String.Concat( checksumiban.Take( 9 ) ) ) % 97, String.Concat( checksumiban.Skip( 9 ) ) );
            }
            while ( checksumiban.Length >= 3 );

            return ( int.Parse( checksumiban ) % 97 ) == 1;
        }

        /// <summary>
        /// Validates the IBANModel of correctness
        /// </summary>
        /// <param name="iban">IBANModel</param>
        /// <param name="converter">Converter to convert the model to ibanstructure</param>
        /// <returns>True / False</returns>
        public bool ValidateIBANModel( Model.IBANModel iban, IIBANConverter converter )
        {
            return this.GetValidationConflict( iban, converter ) == string.Empty;
        }

        /// <summary>
        /// Validates the IBANModel of correctness
        /// </summary>
        /// <param name="iban">IBANModel</param>
        /// <returns>True / False</returns>
        public bool ValidateIBANModel( IBANModel iban )
        {
            return this.ValidateIBANModel( iban, new DefaultIBANConverter() );
        }

        /// <summary>
        /// Gets a message with the validation conflict details
        /// </summary>
        /// <param name="iban">IBAN to validate</param>
        /// <param name="converter">Converter to convert the model to ibanstructure</param>
        /// <returns>Validationconflict details</returns>
        public string GetValidationConflict( Model.IBANModel iban, IIBANConverter converter )
        {
            // First 2 chars are the countrycode
            if ( iban.Country.Length != 2 )
                return String.Format( "Countrycode must be 2 units long. This one is {0} units long", iban.Country.Length );

            // Is the country supported ?
            if ( !CountryCodes.CountryIBANStructes.Select( c => c.CountryShortCode ).Contains( iban.Country ) )
                return String.Format( "Countrycode could not be found in the supported countries list '{0}'", iban.Country );

            var countrySelected = CountryCodes.CountryIBANStructes.Single( c => c.CountryShortCode == iban.Country );

            if ( countrySelected.IBANStructure.Count( r => r == 'r' ) < iban.Branch.Length )
                return String.Format( "Your Branchcode is too long. Branch '{0}' - supported length {1}", iban.Branch, countrySelected.IBANStructure.Count( r => r == 'r' ).ToString() );

            if ( countrySelected.IBANStructure.Count( b => b == 'b' ) < iban.BankCode.Length )
                return String.Format( "Your BankCode is too long. BankCode '{0}' - supported length {1}", iban.BankCode, countrySelected.IBANStructure.Count( b => b == 'b' ).ToString() );

            if ( countrySelected.IBANStructure.Count( a => a == 'a' ) < iban.AccountNumber.Length )
                return String.Format( "Your AccountNumber is too long. AccountNumber '{0}' - supported length {1}", iban.AccountNumber, countrySelected.IBANStructure.Count( a => a == 'a' ).ToString() );

            // Validate IBAN Checksum
            var convertedIBAN = converter.ConvertToElectronicIBAN( iban );

            if ( !this.ValidateIBANChecksum( convertedIBAN ) )
                return String.Format( "The Checksum of this IBAN seems to be wrong. IBAN '{0}'", convertedIBAN );

            if ( convertedIBAN.Length > countrySelected.IBANMaxLength )
                return String.Format( "The IBAN exceeds the maximum allowed length of {0} for this country", countrySelected.IBANMaxLength );

            return string.Empty;
        }

        /// <summary>
        /// Gets a message with the validation conflict details
        /// </summary>
        /// <param name="iban">IBAN to validate</param>
        /// <returns>Validationconflict details</returns>
        public string GetValidationConflict( IBANModel iban )
        {
            return this.GetValidationConflict( iban, new DefaultIBANConverter() );
        }

        /// <summary>
        /// Gets a message with the validation conflict details
        /// </summary>
        /// <param name="iban">IBAN to validate</param>
        /// <returns>Validationconflict details</returns>
        public string GetValidationConflict( string iban )
        {
            if ( !this.ValidateIBANChecksum( iban ) )
            {
                return "The Checksum seems to be invalid";
            }

            return this.GetValidationConflict( iban, new DefaultIBANParser(), new DefaultIBANConverter() );
        }

        /// <summary>
        /// Validates the IBANModel of correctness
        /// </summary>
        /// <param name="iban">IBANModel</param>
        /// <returns>True / False</returns>
        public bool ValidateIBANString( string iban )
        {
            return this.GetValidationConflict( iban ) == string.Empty;
        }

        /// <summary>
        /// Gets a message with the validation conflict details
        /// </summary>
        /// <param name="iban">IBAN to validate</param>
        /// <param name="parser">Custom Parser to parse the string</param>
        /// <returns>Validationconflict details</returns>
        public string GetValidationConflict( string iban, Parser.IIBANParser parser, IIBANConverter converter )
        {
            IBANModel ibanModel;

            try
            {
                ibanModel = parser.Parse( iban );
            }
            catch ( ArgumentException argumentException )
            {
                return argumentException.Message;
            }

            return GetValidationConflict( ibanModel, converter );
        }

        /// <summary>
        /// Validates the IBANModel of correctness
        /// </summary>
        /// <param name="iban">IBANModel</param>
        /// <param name="parser">Custom Parser to parse the string</param>
        /// <returns>True / False</returns>
        public bool ValidateIBANString( string iban, Parser.IIBANParser parser, IIBANConverter converter )
        {
            return this.GetValidationConflict( iban, parser, converter ) == string.Empty;
        }

        #endregion










    }
}
